#ifndef APPLIED_WARP_H_
#define APPLIED_WARP_H_

// AppliedWarp inherits from Warp
#include "Warp.h"

class ReadableImage;

class AppliedWarp : public Warp {
  private:
    // The input image to the warper.
    const ReadableImage* input_;

    // Buffer these values rather than recalculate all the time.
    int output_width_;
    int output_height_;
    double output_radius_;

    // Update the buffered output specifications.
    void updateOutputSpecs();

  public:
    // Initialise with default warp.
    explicit AppliedWarp(const ReadableImage& input);
    // Initialise with custom warp.
    AppliedWarp(const Warp& warp, const ReadableImage& input);

    // Copy constructor.
    AppliedWarp(const AppliedWarp& rhs);
    // Assignment operator.
    AppliedWarp& operator=(const AppliedWarp& rhs);
    // Use default destructor (virtual, same as base class).

    // Allow access to the input image.
    inline const ReadableImage* image() const;
    // Accessors for output specs.
    inline int outputWidth() const;
    inline int outputHeight() const;
    inline double outputRadius() const;

    // Scale controlled by pixel zoom: the least pixel magnification is 1x.
    inline void setScaleAtLeastUnity();
    // Scale controlled by pixel zoom: the greatest pixel magnification is 1x.
    inline void setScaleAtMostUnity();
    // Scale controlled by custom size.
    inline void setScaleToSize(int width, int height);
    // Crop output circle so that no out-of-bounds pixels exist.
    inline void setCrop();
    // Don't crop output circle. Extrapolate pixel colors when needed.
    inline void setFitAllAndExtend();
    // Don't crop output circle. Fill unknown pixels with specified color.
    inline void setFitAllAndFill(const Color& fill);

    // Set scale mode manually.
    inline void setScaleMode(ScaleMode scale_mode);
    // Set crop mode manually.
    inline void setCropMode(CropMode crop_mode);
    // Set whether to extend the colors at the outer radius when not cropped.
    inline void setExtend(bool extend);
    // Set custom size manually.
    inline void setCustomSize(int width, int height);
    // Set custom width manually.
    inline void setCustomWidth(int width);
    // Set custom height manually.
    inline void setCustomHeight(int height);
    // Set background fill manually.
    inline void setFill(const Color& fill);

    // Modifiers for other warp options.
    inline void setInvertX(bool invert_x);
    inline void setInvertY(bool invert_y);
    inline void setPerspectify(bool perspectify);
};

const ReadableImage* AppliedWarp::image() const {
  return input_;
}

int AppliedWarp::outputWidth() const {
  return output_width_;
}

int AppliedWarp::outputHeight() const {
  return output_height_;
}

double AppliedWarp::outputRadius() const {
  return output_radius_;
}

void AppliedWarp::setScaleAtLeastUnity() {
  Warp::setScaleAtLeastUnity();
  updateOutputSpecs();
}

void AppliedWarp::setScaleAtMostUnity() {
  Warp::setScaleAtMostUnity();
  updateOutputSpecs();
}

void AppliedWarp::setScaleToSize(int width, int height) {
  Warp::setScaleToSize(width, height);
  updateOutputSpecs();
}

void AppliedWarp::setCrop() {
  Warp::setCrop();
  updateOutputSpecs();
}

void AppliedWarp::setFitAllAndExtend() {
  Warp::setFitAllAndExtend();
  updateOutputSpecs();
}

void AppliedWarp::setFitAllAndFill(const Color& fill) {
  Warp::setFitAllAndFill(fill);
  updateOutputSpecs();
}

// Set scale mode manually.
void AppliedWarp::setScaleMode(ScaleMode scale_mode) {
  Warp::setScaleMode(scale_mode);
  updateOutputSpecs();
}

// Set crop mode manually.
void AppliedWarp::setCropMode(CropMode crop_mode) {
  Warp::setCropMode(crop_mode);
  updateOutputSpecs();
}

// Set whether to extend the colors at the outer radius when not cropped.
void AppliedWarp::setExtend(bool extend) {
  Warp::setExtend(extend);
  updateOutputSpecs();
}

// Set custom size manually.
void AppliedWarp::setCustomSize(int width, int height) {
  Warp::setCustomSize(width, height);
  updateOutputSpecs();
}

// Set custom width manually.
void AppliedWarp::setCustomWidth(int width) {
  Warp::setCustomWidth(width);
  updateOutputSpecs();
}

// Set custom height manually.
void AppliedWarp::setCustomHeight(int height) {
  Warp::setCustomHeight(height);
  updateOutputSpecs();
}

// Set background fill manually.
void AppliedWarp::setFill(const Color& fill) {
  Warp::setFill(fill);
  updateOutputSpecs();
}

void AppliedWarp::setInvertX(bool invert_x) {
  Warp::setInvertX(invert_x);
  updateOutputSpecs();
}

void AppliedWarp::setInvertY(bool invert_y) {
  Warp::setInvertY(invert_y);
  updateOutputSpecs();
}

void AppliedWarp::setPerspectify(bool perspectify) {
  Warp::setPerspectify(perspectify);
  updateOutputSpecs();
}

#endif
